﻿using BCGPSCommon;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Text; 
using Aliyun.Acs.Core.Profile;
using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Exceptions;
using AppSocketModel;
using System.Text.RegularExpressions;
using Aliyun.Acs.Dysmsapi.Model.V20170525;

namespace AppSocketService.SmsService
{
    public class AliyunSmsService : IAliyunSmsService
    {
        private readonly IConfiguration configuration;
        private readonly IRedisService redisService;

        private readonly string entityKeyFormat = "PassCodeEntity@Phone:{0}";
        private readonly int passExpirationTime = 5;
        //private const string DEFAUTL_SMS_TEXT_FORMAT = "你的验证码为: {0}，为了保证您的账号安全，请在五分钟内完成操作。振飞提示: 千万不能告诉别人哦! ";

        //产品名称:云通信短信API产品,开发者无需替换
        private const string PRODUCT = "Dysmsapi";
        //产品域名,开发者无需替换
        private const string DOMAIN = "dysmsapi.aliyuncs.com";

        public AliyunSmsService(IConfiguration configuration, IRedisService redisService)
        {
            this.configuration = configuration;
            this.redisService = redisService;
        }

        #region 阿里云发送短信方法
        /// <summary>
        /// 查询发送详情
        /// </summary>
        /// <param name="phone">用户手机号</param>
        /// <param name="bizId">发送流水号</param>
        /// <returns></returns>
        private QuerySendDetailsResponse querySendDetails(string phone, string bizId)
        {
            //初始化acsClient,暂不支持region化
            IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", configuration["AliyunPush:AccessKeyID"], configuration["AliyunPush:AccessKeySecret"]);
            DefaultProfile.AddEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
            IAcsClient acsClient = new DefaultAcsClient(profile);
            //组装请求对象
            QuerySendDetailsRequest request = new QuerySendDetailsRequest();
            //必填-号码
            request.PhoneNumber = phone;
            //可选-流水号
            request.BizId = bizId;
            //必填-发送日期 支持30天内记录查询，格式yyyyMMdd       
            request.SendDate = DateTime.Now.ToString("yyyyMMdd");
            //必填-页大小
            request.PageSize = 10;
            //必填-当前页码从1开始计数
            request.CurrentPage = 1;

            QuerySendDetailsResponse querySendDetailsResponse = null;
            try
            {
                querySendDetailsResponse = acsClient.GetAcsResponse(request);
            }
            catch (ServerException e)
            {
                Console.WriteLine(e.ErrorCode);
            }
            catch (ClientException e)
            {
                Console.WriteLine(e.ErrorCode);
            }
            return querySendDetailsResponse;
        }

        /// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="phone">用户手机号</param>
        /// <param name="contentJSONString">发送内容的JSON字符串</param>
        /// <param name="templateCode">发送的短信模板编号</param>
        /// <returns></returns>
        private SendSmsResponse sendSms(string phone, string contentJSONString, string templateCode)
        {
            IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", configuration["AliyunPush:AccessKeyID"], configuration["AliyunPush:AccessKeySecret"]);
            DefaultProfile.AddEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN);
            IAcsClient acsClient = new DefaultAcsClient(profile);
            SendSmsRequest request = new SendSmsRequest();
            SendSmsResponse response = null;
            try
            {

                //必填:待发送手机号。支持以逗号分隔的形式进行批量调用，批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
                request.PhoneNumbers = phone;
                //必填:短信签名-可在短信控制台中找到
                request.SignName = configuration["AliyunSmsService:signatureName"];
                //必填:短信模板-可在短信控制台中找到
                request.TemplateCode = templateCode;
                //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
                request.TemplateParam = contentJSONString;//"{\""+ code +"\"}";
                //可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
                request.OutId = configuration["AliyunSmsService:outId"];
                //请求失败这里会抛ClientException异常
                response = acsClient.GetAcsResponse(request);

            }
            catch (ServerException e)
            {
                Console.WriteLine(e.ErrorCode);
            }
            catch (ClientException e)
            {
                Console.WriteLine(e.ErrorCode);
            }
            return response;
        }
        #endregion

        public GeneratePassCodeDto SendRegisterPassCode(string phone)
        {
            Regex regex = new Regex(RegexPatterns.PHONE);
            if (!regex.IsMatch(phone))
            {
                throw new ArgumentException("请输入正确的手机号");
            }
            string key = string.Format(this.entityKeyFormat, phone);
            string passCode = RandomCode.GeneratePassCode();
            PassCodeEntity entity = new PassCodeEntity
            {
                PhoneNumber = phone,
                PassCode = passCode,
                CreateTime = DateTime.Now,
                ExpireTime = DateTime.Now.AddMinutes(5)
            };

            if (this.redisService.Exists(key))
            {
                PassCodeEntity oldEntity = this.redisService.Get<PassCodeEntity>(key);
                if (oldEntity.CreateTime.AddMinutes(1) > DateTime.Now)
                {
                    throw new ArgumentException("发送验证码过于频繁，请稍后再试！");
                }
            }

            string contentJSONString = "{\"code\":\"" + passCode + "\"}";

            SendSmsResponse response = sendSms(phone, contentJSONString, configuration["AliyunSmsService:templateName:0:identifyingCode"]);
            if (response.Code == null || response.Code != "OK")
            {
                throw new ArgumentException("发送验证码异常，请重试");
            }
            if (!this.redisService.Add(key, entity, TimeSpan.FromMinutes(passExpirationTime)))
            {
                throw new ArgumentException("将数据添加到 Redis 时出错。");
            }
            return new GeneratePassCodeDto
            {
                CreateTime = entity.CreateTime,
                NextAt = entity.CreateTime.AddMinutes(1),
                ExpireTime = entity.ExpireTime
            };
        }

        /// <summary>
        /// 校验验证码
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="passCode">验证码</param>
        /// <returns></returns>
        public bool ValidatePassCode(string phone, string passCode)
        {
            if (string.IsNullOrWhiteSpace(phone))
            {
                throw new ArgumentException("手机号不允许为空!");
            }
            if (string.IsNullOrWhiteSpace(passCode))
            {
                throw new ArgumentException("验证码不能为空!");
            }
            if (passCode.Length != 6)
            {
                throw new ArgumentException("验证码不正确!");
            }
            if (phone.Length != 11)
            {
                throw new ArgumentException("手机号不正确!");
            }
#if DEBUG
            if (passCode == "000000")
            {
                // TODO: 请记住正式发布时移除!!!
                return true;
            }
#endif
            string key = string.Format(this.entityKeyFormat, phone);

            PassCodeEntity entity = null;
            if (this.redisService.Exists(key))
            {
                entity = this.redisService.Get<PassCodeEntity>(key);
            }
            else
            {
                throw new ArgumentException("验证码已过期");
            }

            if (entity.PassCode != passCode)
            {
                return false;
            }

            return true;
        }
    }
}
